package cz.muni.fi.pb162.project.geometry;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Collections;
import java.util.Collection;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * Stores vertexes according to their name (sorted in lexicographic ascending way)
 * 
 * @author Martin Pasko 
 * @version 03_12_2012
 */

public class LabeledPolygon extends SimplePolygon implements PolygonIO
{

    private SortedMap<String, Vertex2D> stringToVertexMap = new TreeMap<String, Vertex2D>();
    private String svert;
    private List<String> isverts = new ArrayList<String>();
    
    public LabeledPolygon() {
    }
    
    /**
     * Stores a vertex with according label.
     * @param label name of the vertex
     * @param vert the vertex to be named
     */
    
    public void addVertex(String label, Vertex2D vert) {
        if(label == null) {
            throw new IllegalArgumentException("no label given");
        }
        if(vert == null) {
            throw new IllegalArgumentException("no vertex given");
        }
        
        stringToVertexMap.put(label, vert);
    }
    
    /**
     * Gives number of vertices.
     * @return number of vertices
     */
    public int getNumVertices() {
        return stringToVertexMap.size();
    }
    
    /**
     * Gives vertex with a given name.
     * @param label name of the vertex
     * @return vertex with a given label
     */
    
    public Vertex2D getVertex(String label) {
        if(!stringToVertexMap.containsKey(label)) {
            throw new IllegalArgumentException("no such label existing");
        }
        return stringToVertexMap.get(label); 
    }
    
    /**
     * Gives vertex with the index in the appropriate lexicographic order given
     * @param index the index of the vertex to be returned
     * @return vertex with the appropriate index
     */
    public Vertex2D getVertex(int index) {
        if(index < 0) {
            throw new IllegalArgumentException("index must be nonnegative");
        }
        int i=0;
        for(String label: stringToVertexMap.keySet()) {
            if(i == index%stringToVertexMap.size()) {
                return stringToVertexMap.get(label);
            }
            i++;
        }
        return null;
    }
    
    /**
     * Gives all names for a vertex with given coordinates.
     * @param vert vertex
     * @return all labels for the vertext
     */
    public Collection<String> getLabels(Vertex2D vert) {
        if(!stringToVertexMap.containsValue(vert)) {
            return Collections.emptySet();
        }
        SortedMap<String, Vertex2D> tempMap = new TreeMap<String, Vertex2D>(stringToVertexMap);
        tempMap.values().retainAll(Collections.singleton(vert));
        return tempMap.keySet();
    }
    
    /**
     * Gives set of vertices ordered naturally.
     * @return set of vertices ordered naturally
     */
    public Collection<Vertex2D> getSortedVertices() {
        Set<Vertex2D> sortedVertexSet = new TreeSet<Vertex2D>(stringToVertexMap.values());              
//        Set<Vertex2D> toRemoveSet = new TreeSet<Vertex2D>();
/*        for(Iterator<Vertex2D> vertIter = sortedVertexSet.iterator(); vertIter.hasNext(); ) {
            Vertex2D vert1 = vertIter.next();
            for(Iterator<Vertex2D> vertIter2 = sortedVertexSet.iterator(); vertIter2.hasNext(); ) {
                Vertex2D vert2 = vertIter2.next();
                if(vert1.equals(vert2)) {
                    toRemoveSet.add(vert2);
                }
            }
        }
        sortedVertexSet.removeAll(toRemoveSet);
*/        
        return sortedVertexSet;
    }
    /**
     * Gives set of vertices ordered by a given comparator.
     * @param comparator to be applied
     * @return set of vertices ordered by a given comparator.
     */
    
    public Collection<Vertex2D> getSortedVertices(Comparator<Vertex2D> comparator) {
        Set<Vertex2D> sortedVertexSet = new TreeSet<Vertex2D>(comparator);
//        Set<Vertex2D> toRemoveSet = new TreeSet<Vertex2D>();
        sortedVertexSet.addAll(stringToVertexMap.values());
/*        for(Iterator<Vertex2D> vertIter = sortedVertexSet.iterator(); vertIter.hasNext(); ) {
            Vertex2D vert1 = vertIter.next();
            for(Iterator<Vertex2D> vertIter2 = sortedVertexSet.iterator(); vertIter2.hasNext(); ) {
                Vertex2D vert2 = vertIter2.next();
                if(vert1.equals(vert2)) {
                    toRemoveSet.add(vert2);
                }
            }
        }
        sortedVertexSet.removeAll(toRemoveSet);
*/
        return sortedVertexSet;
    }
    
    public void write(OutputStream os) throws IOException {
            PrintWriter pw = new PrintWriter(os);
            //PrintStream ps = new PrintStream(os);
            for(String sv: isverts) {
                pw.println(sv);      
            }    
            // ziadne try-catch bloky pri PrintWriter alebo PrintStream!:
            if(pw.checkError()) { throw new IOException(); }
    }
    
    public void write(File file) throws IOException {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            write(fos);
        } finally {
            
            if(fos != null) {
                fos.close();
            }
        }   
    }
    
    public void read(InputStream is) throws IOException {                           // doplnit tu atomickost
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            svert = br.readLine();
            while(svert != null) {
                String label = "";
                String xCoor = "";
                String yCoor = "";
                int i=0;
                while(svert.charAt(i) != ' ') {                                     // urobit cez split
                    xCoor += svert.charAt(i);
                    i++;
                }
                i++;
                while(svert.charAt(i) != ' ') {
                    yCoor += svert.charAt(i);
                    i++;
                }
                i++;
                label = svert.substring(i);
                if(label == "") {
                    throw new IOException("No vertex name given.");
                }
                try {
                    addVertex(label, new Vertex2D(Double.parseDouble(xCoor), Double.parseDouble(yCoor)));
                } catch(NumberFormatException nfe) {
                    throw new IOException("Wrong format of coordinate.", nfe);
                }
                isverts.add(svert);
                svert = br.readLine();
            }           
        } finally {
                //System.err.println("Inside read stream:"+isverts);
                //System.err.println(stringToVertexMap);            
        }
    }
    
    public void read(File file) throws IOException {
        isverts = new ArrayList<String>();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            read(fis);
            
        }
        finally {               
            if(fis != null) {
                fis.close();            
            }
        }    

/*        InputStreamReader fis = null;
        try {
            fis = new InputStreamReader(new FileInputStream(file));
            BufferedReader br = new BufferedReader(fis);
            svert = br.readLine();
            while(svert != null) {
                String label = "";
                String xCoor = "";
                String yCoor = "";
                int i=0;
                while(svert.charAt(i) != ' ') {
                    xCoor += svert.charAt(i);
                    i++;
                }
                i++;
                while(svert.charAt(i) != ' ') {
                    yCoor += svert.charAt(i);
                    i++;
                }
                i++;
                label = svert.substring(i);
                if(label == "") {
                    throw new IOException("No vertex name given.");
                }
                try {
                    addVertex(label, new Vertex2D(Double.parseDouble(xCoor), Double.parseDouble(yCoor)));
                } catch(NumberFormatException nfe) {
                    throw new IOException("Wrong format of coordinate.");
                }
                fverts.add(svert);
                //System.err.println("Inside read file:"+fverts);
                //System.err.println(stringToVertexMap);
                svert = br.readLine();
            }
        } finally { 
            if(fis != null) {
                fis.close();
            }
        }*/     
    }    

}
